Read, Clean, Recode, Merge
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Read
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
## Read files
folder <- "E:/Cinetic idei noi/Cinetic elevi"
file <- "M1 de introdus anotimpuri.xlsx"
setwd(folder)
Data <- rio::import(file.path(folder, file))
## Recode NA
Data <-
Data %>% # sum(is.na(Data)) = 5873
na_if("na") # sum(is.na(Data)) = 6199
## Variable names
nume <- c("Stim", "Varsta_amin", "Ano", "Val", "Viv", "Relv")
toate <- paste(nume, rep(1:15, each = length(nume)), sep = "_")
## Check that all variable names are consistent with column headers
if(toate %in% names(Data)){
cat("All column names are consistent.")
} else {
cat("Column names are NOT consistent. \n")
cat("Missmatches: \n ")
setdiff(toate, names(Data))
}
Sample descriptives
## Number of subjects
## Number of subjects per Protocol
Season Memories and Valence
Make data frames
## Exclude P6 & P7
Data_Season <-
Data %>%
dplyr::filter(!Protocol %in% c(6, 7, 8))
## Melt to Long
# Data_Vara <- # pivot_longer() only in development version of tidyr... dont use now
# Data_Season %>% # devtools::install_github("tidyverse/tidyr")
# tidyr::pivot_longer(
# -c(1:5),
# #cols = starts_with("Ano"),
# names_to = c(".value", "var"),
# names_sep = "_",
# values_drop_na = TRUE
# )
Data_Season_melt <-
Data_Season %>%
gather(variable, value, -c(1:5)) %>%
mutate(group = readr::parse_number(variable)) %>%
mutate(variable = gsub("\\d","",x = variable)) %>%
spread(variable, value) %>%
rename_all(~stringr::str_replace_all(., "_", "")) %>% # delete the "_" at end
mutate(Ano = factor(Ano, levels = c("Vara", "Primavara", "Toamna", "Iarna"))) %>%
mutate_at(vars("Relv", "Val", "Varstaamin", "Viv"), funs(as.numeric(as.character(.))))
## Season data frames
# Data_Vara <-
# Data_Season_melt %>%
# filter(!is.na(Ano)) %>% # delete rows were there is no Ano
# filter(Ano == "Vara")
#
# Data_Primavara <-
# Data_Season_melt %>%
# filter(!is.na(Ano)) %>% # delete rows were there is no Ano
# filter(Ano == "Primavara")
#
# Data_Toamna <-
# Data_Season_melt %>%
# filter(!is.na(Ano)) %>% # delete rows were there is no Ano
# filter(Ano == "Toamna")
#
# Data_Iarna <-
# Data_Season_melt %>%
# filter(!is.na(Ano)) %>% # delete rows were there is no Ano
# filter(Ano == "Iarna")
#
#
# ## Excel downloadable DT tables
# Data_Vara %>%
# select(-Nume) %>%
# DT::datatable(
# extensions = 'Buttons',
# options = list(pageLength = 10,
# scrollX='500px',
# dom = 'Bfrtip',
# buttons = c('excel', "csv")))
#
# Data_Primavara %>%
# select(-Nume) %>%
# DT::datatable(
# extensions = 'Buttons',
# options = list(pageLength = 10,
# scrollX='500px',
# dom = 'Bfrtip',
# buttons = c('excel', "csv")))
#
# Data_Toamna %>%
# select(-Nume) %>%
# DT::datatable(
# extensions = 'Buttons',
# options = list(pageLength = 10,
# scrollX='500px',
# dom = 'Bfrtip',
# buttons = c('excel', "csv")))
#
# Data_Iarna %>%
# select(-Nume) %>%
# DT::datatable(
# extensions = 'Buttons',
# options = list(pageLength = 10,
# scrollX='500px',
# dom = 'Bfrtip',
# buttons = c('excel', "csv")))
cat("### Melt to Long Format")
Define Function for Plots
## Function for Ano Bar Plot
my_comparisons <-
gtools::combinations(n = length(unique(Data_Season_melt_nona$Ano)), r = 2, v = as.character(Data_Season_melt_nona$Ano), repeats.allowed = FALSE) %>%
as.data.frame() %>%
mutate_if(is.factor, as.character) %>%
purrr::pmap(list) %>%
lapply(unlist)
func_plot_ano <- function(df, y_var, y_var_lab, label.y_set = 7, yticks.by_set = 1, facet = FALSE){
if(facet){
facet <- "Protocol"
}else{
facet <- NULL
}
p <-
df %>%
ggpubr::ggbarplot(x = "Ano", y = y_var,
add = "mean_se",
color = "black", fill = "lightgray",
xlab = "Anotimp", ylab = y_var_lab,
label = TRUE, lab.nb.digits = 2, lab.pos= "in",
facet.by = facet) +
stat_compare_means(method = "anova",
label.x = 0.9, label.y = label.y_set) +
stat_compare_means(comparisons = my_comparisons,
label = "p.signif", method = "t.test", paired = FALSE, na.rm = TRUE)
ggpar(p, yticks.by = yticks.by_set) # the rating scale is 1-7
}
## Dodged
func_dodged_ano <- function(df, y_var, y_var_lab, facet = FALSE){
y_var<- sym(y_var)
if(facet) {
df <-
df %>%
mutate(Protocol = paste0("Protocol ", Protocol)) %>%
group_by(Protocol)
}
p <-
df %>%
dplyr::count(Ano, !!y_var) %>% # Group by, then count number in each group
mutate(pct = prop.table(n)) %>% # Calculate percent within each var
mutate(Val_fac = as.factor(!!y_var)) %>%
ggplot(aes(x = Ano, y = pct, fill = Val_fac, label = scales::percent(pct))) +
geom_col(position = 'dodge') +
geom_text(position = position_dodge(width = .9), # move to center of bars
vjust = -0.5, # nudge above top of bar
size = 3) +
scale_y_continuous(labels = scales::percent) +
{if(facet) facet_wrap(~Protocol, scales = "free", ncol = 1, nrow = 8)} +
ggtitle(y_var_lab) +
xlab("Anotimp") + ylab("Percentage %") +
guides(fill = guide_legend(title = "Value", nrow = 1)) +
scale_fill_grey(start = 0.8, end = 0.2, na.value = "red", aesthetics = "fill") +
theme(legend.position = "bottom", legend.direction = "horizontal",
legend.justification = c(0, 1), panel.border = element_rect(fill = NA, colour = "black"))
p
}
Plots of Seasons
## Test for Val -- works well
# Data_Season_melt_nona %>%
# ggpubr::ggbarplot(x = "Ano", y = "Val",
# add = "mean_se",
# color = "black", fill = "lightgray",
# xlab = "Anotimp", ylab = "Valenta",
# label = TRUE, lab.nb.digits = 2, lab.pos= "in") +
# stat_compare_means(method = "anova",
# label.x = 0.9, label.y = 7) +
# stat_compare_means(comparisons = my_comparisons,
# label = "p.signif", method = "t.test", paired = FALSE, na.rm = TRUE)
func_plot_ano(Data_Season_melt_nona, "Val", "Valenta")
Plots of Seasons by Protocol
Plots with proportion of values
Stacked: Val
Data_Season_melt_nona %>%
dplyr::count(Ano, Val) %>% # Group by, then count number in each group
mutate(pct = n/sum(n)) %>% # Calculate percent within each var; could use prop.table(n)
mutate(Val_fac = as.factor(Val)) %>%
ggplot(aes(Ano, n, fill = Val_fac)) +
geom_bar(stat = "identity") +
geom_text(aes(label = paste0(sprintf("%1.1f", pct*100), "%"), size = scales::rescale(pct, to=c(2, 5))),
position = position_stack(vjust=0.5), show.legend = FALSE) +
guides(fill = guide_legend(title = "Value", nrow = 1)) +
theme(legend.position = "bottom", legend.direction = "horizontal", legend.justification = c(0, 1))
# # Dodged - Test for Val -- works well
# Data_Season_melt_nona %>%
# dplyr::count(Ano, Val) %>% # Group by, then count number in each group
# mutate(pct = prop.table(n)) %>% # Calculate percent within each var
# mutate(Val_fac = as.factor(Val)) %>%
# ggplot(aes(x = Ano, y = pct, fill = Val_fac, label = scales::percent(pct))) +
# geom_col(position = 'dodge') +
# geom_text(position = position_dodge(width = .9), # move to center of bars
# vjust = -0.5, # nudge above top of bar
# size = 3) +
# scale_y_continuous(labels = scales::percent) +
# xlab("Anotimp") + ylab("Percentage %") +
# guides(fill = guide_legend(title = "Value", nrow = 1)) +
# scale_fill_grey(start = 0.8, end = 0.2, na.value = "red", aesthetics = "fill") +
# theme(legend.position = "bottom", legend.direction = "horizontal", legend.justification = c(0, 1))
cat("### Dodged: Val, Relev, Vivid")
Dodged: Val, Relev, Vivid
Plots with proportion of values by Protocol
Likert Plots for Season
# Proportions and z-scores
Prop_val <-
Data_Season_melt_nona %>%
dplyr::select(ID, Protocol, Ano, Val) %>%
group_by(Ano) %>%
mutate(
Val = as.factor(Val),
Val = forcats::fct_collapse(Val, low = c("1", "2", "3"), neutral = "4", high = c("5", "6", "7"))
) %>%
dplyr::count(Val) %>%
mutate(total = sum(n),
perc = 100*n/total)
cat("### Proportions - compared to 0.5 probability")
Proportions - compared to 0.5 probability
Proportions - Multiple comparisons
Pairwise comparisons using Pairwise comparison of proportions
Pairwise comparisons using Pairwise comparison of proportions (Fisher exact)
Proportions - Plot of Low-Neutral-High
Likert_val <-
Data_Season_melt_nona %>%
dplyr::select(ID, Protocol, group, Ano, Val) %>%
spread(key = Ano, value = Val) %>%
mutate_at(vars("Vara", "Primavara", "Toamna", "Iarna"), ~as.factor(.))
# Plots # library(likert)
Likertobj_Val <- likert(Likert_val[, c("Vara", "Primavara", "Toamna", "Iarna")], nlevels = 7) # here are percentages
Likertobj_Val_perc <- Likertobj_Val$results
# check if same with Prop dataframe above; or prop.table(table(Likert_val$Vara))
plot(Likertobj_Val, type = "bar",
centered = TRUE, center = 4, include.center = TRUE, # "4" is neutral
wrap = 30, low.color = 'burlywood', high.color = 'maroon') +
guides(fill = guide_legend(nrow = 1))
Relationship: Frequency of remembering - Valence
Scatter plot with correlation coefficient for all Seasons
Scatter plot with correlation coefficient for each Season
ggpubr::ggscatter(Anofreq_Val, x = "Freq_Ano", y = "Mean_Val",
color = "Ano", palette = "jco",
add = "reg.line", conf.int = TRUE,
xlim = c(0, 15), ylim = c(0, 8)) +
stat_cor(aes(color = Ano), method = "pearson", label.x = 11)
Most negative Valence (ones)
Proportions - compared to 0.5 probability
Proportions - Multiple comparisons
Pairwise comparisons using Pairwise comparison of proportions
Pairwise comparisons using Pairwise comparison of proportions (Fisher exact)
Proportions - Plot of Low-Neutral-High
Likert_val_all <-
Data_Season_melt_nona %>%
dplyr::select(ID, Protocol, group, Ano, Val) %>%
spread(key = Ano, value = Val) %>%
mutate_at(vars("Vara", "Primavara", "Toamna", "Iarna"), ~as.factor(.))
# Plots # library(likert)
Likertobj_Val_all <- likert(Likert_val[, c("Vara", "Primavara", "Toamna", "Iarna")], nlevels = 7) # here are percentages
Likertobj_Val_perc_all <- Likertobj_Val$results
plot(Likertobj_Val_all, type = "heat") +
ggtitle("Mean (SD) vs Percetages of Valence Ratings") +
theme(legend.position = 'none')
Session Info
R version 3.6.1 (2019-07-05)
Platform: x86_64-w64-mingw32/x64 (64-bit)
Running under: Windows 8.1 x64 (build 9600)
Matrix products: default
locale:
[1] LC_COLLATE=Romanian_Romania.1250 LC_CTYPE=Romanian_Romania.1250 LC_MONETARY=Romanian_Romania.1250 LC_NUMERIC=C
[5] LC_TIME=Romanian_Romania.1250
attached base packages:
[1] stats graphics grDevices utils datasets methods base
other attached packages:
[1] likert_1.3.5 xtable_1.8-4 fmsb_0.6.3 rio_0.5.16 scales_1.0.0
[6] ggpubr_0.2 magrittr_1.5 tadaatoolbox_0.16.1 summarytools_0.8.8 rstatix_0.2.0
[11] broom_0.5.2 PerformanceAnalytics_1.5.2 xts_0.11-2 zoo_1.8-4 psych_1.8.12
[16] plyr_1.8.4 forcats_0.4.0 stringr_1.4.0 dplyr_0.8.3 purrr_0.3.2
[21] readr_1.3.1 tidyr_1.0.0 tibble_2.1.3 ggplot2_3.2.1 tidyverse_1.2.1
[26] papaja_0.1.0.9842 pacman_0.5.1
loaded via a namespace (and not attached):
[1] colorspace_1.4-1 ggsignif_0.4.0 pryr_0.1.4 ellipsis_0.3.0 rstudioapi_0.8 DT_0.5 mvtnorm_1.0-11
[8] lubridate_1.7.4 xml2_1.2.0 codetools_0.2-16 mnormt_1.5-5 knitr_1.25 zeallot_0.1.0 pixiedust_0.8.6
[15] jsonlite_1.6 shiny_1.2.0 compiler_3.6.1 httr_1.4.0 backports_1.1.4 assertthat_0.2.1 Matrix_1.2-17
[22] lazyeval_0.2.2 cli_1.1.0 later_0.7.5 htmltools_0.3.6 tools_3.6.1 gtable_0.3.0 glue_1.3.1
[29] reshape2_1.4.3 Rcpp_1.0.2 carData_3.0-2 cellranger_1.1.0 vctrs_0.2.0 nlme_3.1-140 crosstalk_1.0.0
[36] xfun_0.9 openxlsx_4.1.0 rvest_0.3.2 mime_0.7 lifecycle_0.1.0 gtools_3.8.1 MASS_7.3-51.4
[43] hms_0.5.1 promises_1.0.1 parallel_3.6.1 expm_0.999-3 pwr_1.2-2 yaml_2.2.0 curl_3.2
[50] gridExtra_2.3 pander_0.6.3 stringi_1.4.3 nortest_1.0-4 boot_1.3-22 zip_1.0.0 rlang_0.4.0
[57] pkgconfig_2.0.3 matrixStats_0.54.0 bitops_1.0-6 lattice_0.20-38 labeling_0.3 rapportools_1.0 htmlwidgets_1.3
[64] tidyselect_0.2.5 ggsci_2.9 R6_2.4.0 DescTools_0.99.29 generics_0.0.2 pillar_1.4.2 haven_2.1.1
[71] foreign_0.8-71 withr_2.1.2 abind_1.4-5 RCurl_1.95-4.11 modelr_0.1.5 crayon_1.3.4 car_3.0-2
[78] viridis_0.5.1 grid_3.6.1 readxl_1.1.0 data.table_1.11.8 digest_0.6.21 httpuv_1.4.5 munsell_0.5.0
[85] viridisLite_0.3.0 quadprog_1.5-5
A work by Claudiu Papasteri
claudiu.papasteri@gmail.com
LS0tDQp0aXRsZTogIjxicj4gQW5hbHlzZXMgZm9yIE0uMS4gd2l0aG91dCBQOCAoQXV0b2Jpb2dyYXBoaWNhbCBNZW1vcmllcykiIA0Kc3VidGl0bGU6ICJGb2N1cyBvbiBTZWFzb25zIC0gaW5kaXZpZHVhbCBzdGltdWxpIg0KYXV0aG9yOiAiPGJyPiBDbGF1ZGl1IFBhcGFzdGVyaSINCmRhdGU6ICJgciBmb3JtYXQoU3lzLnRpbWUoKSwgJyVkICVtICVZJylgIg0Kb3V0cHV0OiANCiAgICBodG1sX25vdGVib29rOg0KICAgICAgICAgICAgY29kZV9mb2xkaW5nOiBoaWRlDQogICAgICAgICAgICB0b2M6IHRydWUNCiAgICAgICAgICAgIHRvY19kZXB0aDogMg0KICAgICAgICAgICAgbnVtYmVyX3NlY3Rpb25zOiB0cnVlDQogICAgICAgICAgICB0aGVtZTogc3BhY2VsYWINCiAgICAgICAgICAgIGhpZ2hsaWdodDogdGFuZ28NCiAgICAgICAgICAgIGZvbnQtZmFtaWx5OiBBcmlhbA0KICAgICAgICAgICAgZmlnX3dpZHRoOiAxMA0KICAgICAgICAgICAgZmlnX2hlaWdodDogOQ0KICAgICMgd29yZF9kb2N1bWVudCAgICAgICAgDQogICAgIyBwZGZfZG9jdW1lbnQ6IA0KICAgICAgICAgICAgIyB0b2M6IHRydWUNCiAgICAgICAgICAgICMgdG9jX2RlcHRoOiAyDQogICAgICAgICAgICAjIG51bWJlcl9zZWN0aW9uczogdHJ1ZQ0KICAgICAgICAgICAgIyBmb250c2l6ZTogMTFwdA0KICAgICAgICAgICAgIyBnZW9tZXRyeTogbWFyZ2luPTFpbg0KICAgICAgICAgICAgIyBmaWdfd2lkdGg6IDcNCiAgICAgICAgICAgICMgZmlnX2hlaWdodDogNg0KICAgICAgICAgICAgIyBmaWdfY2FwdGlvbjogdHJ1ZQ0KICAgICMgZ2l0aHViX2RvY3VtZW50OiANCiAgICAgICAgICAgICMgdG9jOiB0cnVlDQogICAgICAgICAgICAjIHRvY19kZXB0aDogMg0KICAgICAgICAgICAgIyBodG1sX3ByZXZpZXc6IGZhbHNlDQogICAgICAgICAgICAjIGZpZ193aWR0aDogNQ0KICAgICAgICAgICAgIyBmaWdfaGVpZ2h0OiA1DQogICAgICAgICAgICAjIGRldjoganBlZw0KLS0tDQoNCg0KPCEtLSBTZXR1cCAtLT4NCg0KDQpgYGB7ciBzZXR1cCwgaW5jbHVkZT1GQUxTRX0NCiMga2ludHIgb3B0aW9ucw0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KA0KICBjb21tZW50ID0gIiMiLA0KICBjb2xsYXBzZSA9IFRSVUUsDQogIGVjaG8gPSBUUlVFLCANCiAgd2FybmluZyA9IEZBTFNFLCBtZXNzYWdlID0gRkFMU0UsIGVycm9yID0gRkFMU0UsDQogIGNhY2hlID0gVFJVRSAgICAgICAjIGVjaG8gPSBGYWxzZSBmb3IgZ2l0aHViX2RvY3VtZW50LCBidXQgd2lsbCBiZSBmb2xkZWQgaW4gaHRtbF9ub3RlYm9vaw0KKQ0KDQojIEdlbmVyYWwgUiBvcHRpb25zIGFuZCBpbmZvDQpzZXQuc2VlZCgxMTEpICAgICAgICAgICAgICAgIyBpbiBjYXNlIHdlIHVzZSByYW5kb21pemVkIHByb2NlZHVyZXMgICAgICAgDQpvcHRpb25zKHNjaXBlbiA9IDk5OSkgICAgICAgIyBwb3NpdGl2ZSB2YWx1ZXMgYmlhcyB0b3dhcmRzIGZpeGVkIGFuZCBuZWdhdGl2ZSB0b3dhcmRzIHNjaWVudGlmaWMgbm90YXRpb24NCg0KIyBMb2FkIHBhY2thZ2VzDQppZiAoIXJlcXVpcmUoInBhY21hbiIpKSBpbnN0YWxsLnBhY2thZ2VzKCJwYWNtYW4iKQ0KcGFja2FnZXMgPC0gYygNCiAgInBhcGFqYSIsDQogICJ0aWR5dmVyc2UiLCAicGx5ciIsICAgICAgDQogICJwc3ljaCIsICJQZXJmb3JtYW5jZUFuYWx5dGljcyIsICAgICAgICAgIA0KICAiYnJvb20iLCAicnN0YXRpeCIsDQogICJzdW1tYXJ5dG9vbHMiLCAidGFkYWF0b29sYm94IiwgICAgICAgICAgIA0KICAiZ2dwbG90MiIsICJnZ3B1YnIiLCAic2NhbGVzIiwgICAgICAgIA0KICAicmlvIiwNCiAgImZtc2IiLCAibGlrZXJ0Ig0KICAjICwgLi4uDQopDQppZiAoIXJlcXVpcmUoInBhY21hbiIpKSBpbnN0YWxsLnBhY2thZ2VzKCJwYWNtYW4iKQ0KcGFjbWFuOjpwX2xvYWQoY2hhciA9IHBhY2thZ2VzKQ0KDQojIFRoZW1lcyBmb3IgZ2dwbG90MiBwbG90aW5nIChoZXJlIHVzZWQgQVBBIHN0eWxlKQ0KdGhlbWVfc2V0KHRoZW1lX2FwYSgpKQ0KDQoNCiMgVGFibGVzIGtuaXR0aW5nIHRvIFdvcmQNCmRvYy50eXBlIDwtIGtuaXRyOjpvcHRzX2tuaXQkZ2V0KCdybWFya2Rvd24ucGFuZG9jLnRvJykgICMgdGhlbiBmb3JtYXQgdGFibGVzIHVzaW5nIGFuIGlmIHN0YXRlbWVudCBsaWtlOg0KIyBpZiAoZG9jLnR5cGUgPT0gImRvY3giKSB7IHBhbmRlcjo6cGFuZGVyKGRmKSB9IGVsc2UgeyBrbml0cjo6a2FibGUoZGYpIH0NCmBgYA0KDQoNCg0KDQoNCjwhLS0gUmVwb3J0IC0tPg0KDQoNCiMgUmVhZCwgQ2xlYW4sIFJlY29kZSwgTWVyZ2UNCg0KYGBge3IgcmVkX2NsZWFuX3JlY29kZV9tZXJnZSwgcmVzdWx0cz0naGlkZSd9DQojfn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fg0KIyBSZWFkDQojfn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fg0KDQojIyBSZWFkIGZpbGVzDQpmb2xkZXIgPC0gIkU6L0NpbmV0aWMgaWRlaSBub2kvQ2luZXRpYyBlbGV2aSINCmZpbGUgPC0gIk0xIGRlIGludHJvZHVzIGFub3RpbXB1cmkueGxzeCINCg0Kc2V0d2QoZm9sZGVyKQ0KRGF0YSA8LSByaW86OmltcG9ydChmaWxlLnBhdGgoZm9sZGVyLCBmaWxlKSkNCg0KIyMgUmVjb2RlIE5BDQpEYXRhIDwtIA0KICBEYXRhICU+JSAgICAgICAgICAgICAjIHN1bShpcy5uYShEYXRhKSkgPSA1ODczDQogIG5hX2lmKCJuYSIpICAgICAgICAgICMgc3VtKGlzLm5hKERhdGEpKSA9IDYxOTkNCg0KDQojIyBWYXJpYWJsZSBuYW1lcw0KbnVtZSA8LSBjKCJTdGltIiwgIlZhcnN0YV9hbWluIiwgIkFubyIsICJWYWwiLCAiVml2IiwgIlJlbHYiKQ0KdG9hdGUgPC0gcGFzdGUobnVtZSwgcmVwKDE6MTUsIGVhY2ggPSBsZW5ndGgobnVtZSkpLCBzZXAgPSAiXyIpDQoNCiMjIENoZWNrIHRoYXQgYWxsIHZhcmlhYmxlIG5hbWVzIGFyZSBjb25zaXN0ZW50IHdpdGggY29sdW1uIGhlYWRlcnMNCmlmKHRvYXRlICVpbiUgbmFtZXMoRGF0YSkpew0KICBjYXQoIkFsbCBjb2x1bW4gbmFtZXMgYXJlIGNvbnNpc3RlbnQuIikNCn0gZWxzZSB7DQogIGNhdCgiQ29sdW1uIG5hbWVzIGFyZSBOT1QgY29uc2lzdGVudC4gXG4iKQ0KICBjYXQoIk1pc3NtYXRjaGVzOiBcbiAiKQ0KICBzZXRkaWZmKHRvYXRlLCBuYW1lcyhEYXRhKSkNCn0NCmBgYA0KDQoNCiMgU2FtcGxlIGRlc2NyaXB0aXZlcw0KDQpgYGB7ciBzYW1wbGVfZGVzY30NCmNhdCgiIyMgTnVtYmVyIG9mIHN1YmplY3RzIikNCkRhdGEgJT4lIA0KIGRwbHlyOjpzdW1tYXJpc2UoY291bnQgPSBkcGx5cjo6bl9kaXN0aW5jdChJRCkpDQoNCmNhdCgiIyMgTnVtYmVyIG9mIHN1YmplY3RzIHBlciBQcm90b2NvbCIpDQpEYXRhICU+JQ0KIGdyb3VwX2J5KFByb3RvY29sKSAlPiUNCiBkcGx5cjo6c3VtbWFyaXNlKGNvdW50ID0gZHBseXI6Om5fZGlzdGluY3QoSUQpKQ0KYGBgDQoNCg0KIyBTZWFzb24gTWVtb3JpZXMgYW5kIFZhbGVuY2UNCg0KIyMgTWFrZSBkYXRhIGZyYW1lcw0KDQpgYGB7ciBkZl9zZWFuc29uLCByZXN1bHRzPSdhc2lzJywgd2FybmluZz1GQUxTRX0NCiMjIEV4Y2x1ZGUgUDYgJiBQNw0KRGF0YV9TZWFzb24gPC0gDQogIERhdGEgJT4lDQogIGRwbHlyOjpmaWx0ZXIoIVByb3RvY29sICVpbiUgYyg2LCA3LCA4KSkNCg0KIyMgTWVsdCB0byBMb25nDQoNCiMgRGF0YV9WYXJhIDwtICAgICAgICAgICAgICAgICAgICAgICAgICAjIHBpdm90X2xvbmdlcigpIG9ubHkgaW4gZGV2ZWxvcG1lbnQgdmVyc2lvbiBvZiB0aWR5ci4uLiBkb250IHVzZSBub3cNCiMgICBEYXRhX1NlYXNvbiAlPiUgICAgICAgICAgICAgICAgICAgICAjIGRldnRvb2xzOjppbnN0YWxsX2dpdGh1YigidGlkeXZlcnNlL3RpZHlyIikNCiMgICB0aWR5cjo6cGl2b3RfbG9uZ2VyKA0KIyAgICAgLWMoMTo1KSwNCiMgICAgICNjb2xzID0gc3RhcnRzX3dpdGgoIkFubyIpLCANCiMgICAgIG5hbWVzX3RvID0gYygiLnZhbHVlIiwgInZhciIpLCANCiMgICAgIG5hbWVzX3NlcCA9ICJfIiwgDQojICAgICB2YWx1ZXNfZHJvcF9uYSA9IFRSVUUNCiMgICApDQoNCkRhdGFfU2Vhc29uX21lbHQgPC0gICAgICAgICAgICAgICAgICAgICAgICAgDQogIERhdGFfU2Vhc29uICU+JQ0KICBnYXRoZXIodmFyaWFibGUsIHZhbHVlLCAtYygxOjUpKSAlPiUNCiAgbXV0YXRlKGdyb3VwID0gcmVhZHI6OnBhcnNlX251bWJlcih2YXJpYWJsZSkpICU+JQ0KICBtdXRhdGUodmFyaWFibGUgPSBnc3ViKCJcXGQiLCIiLHggPSB2YXJpYWJsZSkpICU+JQ0KICBzcHJlYWQodmFyaWFibGUsIHZhbHVlKSAlPiUNCiAgcmVuYW1lX2FsbCh+c3RyaW5ncjo6c3RyX3JlcGxhY2VfYWxsKC4sICJfIiwgIiIpKSAlPiUgICAgICAgICAgICMgZGVsZXRlIHRoZSAiXyIgYXQgZW5kDQogIG11dGF0ZShBbm8gPSBmYWN0b3IoQW5vLCBsZXZlbHMgPSBjKCJWYXJhIiwgIlByaW1hdmFyYSIsICJUb2FtbmEiLCAiSWFybmEiKSkpICU+JQ0KICBtdXRhdGVfYXQodmFycygiUmVsdiIsICJWYWwiLCAiVmFyc3RhYW1pbiIsICJWaXYiKSwgZnVucyhhcy5udW1lcmljKGFzLmNoYXJhY3RlciguKSkpKQ0KICANCiMjIFNlYXNvbiBkYXRhIGZyYW1lcw0KIyBEYXRhX1ZhcmEgPC0NCiMgICBEYXRhX1NlYXNvbl9tZWx0ICU+JQ0KIyAgIGZpbHRlcighaXMubmEoQW5vKSkgJT4lICAgICAgICAgICAgICAgICMgZGVsZXRlIHJvd3Mgd2VyZSB0aGVyZSBpcyBubyBBbm8NCiMgICBmaWx0ZXIoQW5vID09ICJWYXJhIikNCiMgDQojIERhdGFfUHJpbWF2YXJhIDwtDQojICAgRGF0YV9TZWFzb25fbWVsdCAlPiUNCiMgICBmaWx0ZXIoIWlzLm5hKEFubykpICU+JSAgICAgICAgICAgICAgICAjIGRlbGV0ZSByb3dzIHdlcmUgdGhlcmUgaXMgbm8gQW5vDQojICAgZmlsdGVyKEFubyA9PSAiUHJpbWF2YXJhIikNCiMgDQojIERhdGFfVG9hbW5hIDwtDQojICAgRGF0YV9TZWFzb25fbWVsdCAlPiUNCiMgICBmaWx0ZXIoIWlzLm5hKEFubykpICU+JSAgICAgICAgICAgICAgICAjIGRlbGV0ZSByb3dzIHdlcmUgdGhlcmUgaXMgbm8gQW5vDQojICAgZmlsdGVyKEFubyA9PSAiVG9hbW5hIikNCiMgDQojIERhdGFfSWFybmEgPC0NCiMgICBEYXRhX1NlYXNvbl9tZWx0ICU+JQ0KIyAgIGZpbHRlcighaXMubmEoQW5vKSkgJT4lICAgICAgICAgICAgICAgICMgZGVsZXRlIHJvd3Mgd2VyZSB0aGVyZSBpcyBubyBBbm8NCiMgICBmaWx0ZXIoQW5vID09ICJJYXJuYSIpDQojIA0KIyANCiMgIyMgRXhjZWwgZG93bmxvYWRhYmxlIERUIHRhYmxlcw0KIyBEYXRhX1ZhcmEgJT4lICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgDQojICAgc2VsZWN0KC1OdW1lKSAlPiUNCiMgICAgIERUOjpkYXRhdGFibGUoICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIA0KIyAgICAgICBleHRlbnNpb25zID0gJ0J1dHRvbnMnLA0KIyAgICAgICBvcHRpb25zID0gbGlzdChwYWdlTGVuZ3RoID0gMTAsDQojICAgICAgICAgICAgICAgICAgICAgIHNjcm9sbFg9JzUwMHB4JywgDQojICAgICAgICAgICAgICAgICAgICAgIGRvbSA9ICdCZnJ0aXAnLCANCiMgICAgICAgICAgICAgICAgICAgICAgYnV0dG9ucyA9IGMoJ2V4Y2VsJywgImNzdiIpKSkNCiMgDQojIERhdGFfUHJpbWF2YXJhICU+JSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIA0KIyAgIHNlbGVjdCgtTnVtZSkgJT4lDQojICAgICBEVDo6ZGF0YXRhYmxlKCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICANCiMgICAgICAgZXh0ZW5zaW9ucyA9ICdCdXR0b25zJywNCiMgICAgICAgb3B0aW9ucyA9IGxpc3QocGFnZUxlbmd0aCA9IDEwLA0KIyAgICAgICAgICAgICAgICAgICAgICBzY3JvbGxYPSc1MDBweCcsIA0KIyAgICAgICAgICAgICAgICAgICAgICBkb20gPSAnQmZydGlwJywgDQojICAgICAgICAgICAgICAgICAgICAgIGJ1dHRvbnMgPSBjKCdleGNlbCcsICJjc3YiKSkpDQojIA0KIyBEYXRhX1RvYW1uYSAlPiUgICAgICAgICAgICAgICAgICAgICAgICAgICAgICANCiMgICBzZWxlY3QoLU51bWUpICU+JQ0KIyAgICAgRFQ6OmRhdGF0YWJsZSggICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgDQojICAgICAgIGV4dGVuc2lvbnMgPSAnQnV0dG9ucycsDQojICAgICAgIG9wdGlvbnMgPSBsaXN0KHBhZ2VMZW5ndGggPSAxMCwNCiMgICAgICAgICAgICAgICAgICAgICAgc2Nyb2xsWD0nNTAwcHgnLCANCiMgICAgICAgICAgICAgICAgICAgICAgZG9tID0gJ0JmcnRpcCcsIA0KIyAgICAgICAgICAgICAgICAgICAgICBidXR0b25zID0gYygnZXhjZWwnLCAiY3N2IikpKQ0KIyANCiMgRGF0YV9JYXJuYSAlPiUgICAgICAgICAgICAgICAgICAgICAgICAgICAgICANCiMgICBzZWxlY3QoLU51bWUpICU+JQ0KIyAgICAgRFQ6OmRhdGF0YWJsZSggICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgDQojICAgICAgIGV4dGVuc2lvbnMgPSAnQnV0dG9ucycsDQojICAgICAgIG9wdGlvbnMgPSBsaXN0KHBhZ2VMZW5ndGggPSAxMCwNCiMgICAgICAgICAgICAgICAgICAgICAgc2Nyb2xsWD0nNTAwcHgnLCANCiMgICAgICAgICAgICAgICAgICAgICAgZG9tID0gJ0JmcnRpcCcsIA0KIyAgICAgICAgICAgICAgICAgICAgICBidXR0b25zID0gYygnZXhjZWwnLCAiY3N2IikpKQ0KDQoNCmNhdCgiIyMjIE1lbHQgdG8gTG9uZyBGb3JtYXQiKQ0KRGF0YV9TZWFzb25fbWVsdCAlPiUNCiAgZHBseXI6OnNlbGVjdCgtTnVtZSkgJT4lDQogICAgRFQ6OmRhdGF0YWJsZSgNCiAgICAgIGV4dGVuc2lvbnMgPSAnQnV0dG9ucycsDQogICAgICBvcHRpb25zID0gbGlzdChwYWdlTGVuZ3RoID0gMTAsDQogICAgICAgICAgICAgICAgICAgICBzY3JvbGxYPSc1MDBweCcsDQogICAgICAgICAgICAgICAgICAgICBkb20gPSAnQmZydGlwJywNCiAgICAgICAgICAgICAgICAgICAgIGJ1dHRvbnMgPSBjKCdleGNlbCcsICJjc3YiKSkpDQoNCmNhdCgiIyMjIFdpZGUgRm9ybWF0IikNCkRhdGFfU2Vhc29uICU+JQ0KICBkcGx5cjo6c2VsZWN0KC1OdW1lKSAlPiUNCiAgICBEVDo6ZGF0YXRhYmxlKA0KICAgICAgZXh0ZW5zaW9ucyA9ICdCdXR0b25zJywNCiAgICAgIG9wdGlvbnMgPSBsaXN0KHBhZ2VMZW5ndGggPSAxMCwNCiAgICAgICAgICAgICAgICAgICAgIHNjcm9sbFg9JzUwMHB4JywNCiAgICAgICAgICAgICAgICAgICAgIGRvbSA9ICdCZnJ0aXAnLA0KICAgICAgICAgICAgICAgICAgICAgYnV0dG9ucyA9IGMoJ2V4Y2VsJywgImNzdiIpKSkNCg0KDQojIyBEYXRhIEZyYW1lIGZvciBQbG90cw0KRGF0YV9TZWFzb25fbWVsdF9ub25hIDwtDQogIERhdGFfU2Vhc29uX21lbHQgJT4lDQogIGZpbHRlcighaXMubmEoQW5vKSkNCg0KDQpjYXQoIiMjIyBXaWRlIEZvcm1hdCBmb3IgQW5vIH4gVmFsZW5jZSIpDQpEYXRhX1NlYXNvbl9tZWx0X25vbmEgJT4lDQogIGRwbHlyOjpzZWxlY3QoSUQsIEFubywgVmFsKSAlPiUNCiAgcm93bmFtZXNfdG9fY29sdW1uKCkgJT4lDQogIHNwcmVhZChrZXkgPSBBbm8sIHZhbHVlID0gVmFsKSAlPiUNCiAgYXJyYW5nZShJRCkgJT4lDQogICAgRFQ6OmRhdGF0YWJsZSgNCiAgICAgIGV4dGVuc2lvbnMgPSAnQnV0dG9ucycsDQogICAgICBvcHRpb25zID0gbGlzdChwYWdlTGVuZ3RoID0gMTAsDQogICAgICAgICAgICAgICAgICAgICBzY3JvbGxYPSc1MDBweCcsDQogICAgICAgICAgICAgICAgICAgICBkb20gPSAnQmZydGlwJywNCiAgICAgICAgICAgICAgICAgICAgIGJ1dHRvbnMgPSBjKCdleGNlbCcsICJjc3YiKSkpDQpgYGANCg0KDQojIyBEZWZpbmUgRnVuY3Rpb24gZm9yIFBsb3RzDQoNCmBgYHtyIGRlZl9mdW5jX3Bsb3R9DQojIyBGdW5jdGlvbiBmb3IgQW5vIEJhciBQbG90DQpteV9jb21wYXJpc29ucyA8LSANCiAgZ3Rvb2xzOjpjb21iaW5hdGlvbnMobiA9IGxlbmd0aCh1bmlxdWUoRGF0YV9TZWFzb25fbWVsdF9ub25hJEFubykpLCByID0gMiwgdiA9IGFzLmNoYXJhY3RlcihEYXRhX1NlYXNvbl9tZWx0X25vbmEkQW5vKSwgcmVwZWF0cy5hbGxvd2VkID0gRkFMU0UpICU+JQ0KICBhcy5kYXRhLmZyYW1lKCkgJT4lIA0KICBtdXRhdGVfaWYoaXMuZmFjdG9yLCBhcy5jaGFyYWN0ZXIpICU+JQ0KICBwdXJycjo6cG1hcChsaXN0KSAlPiUgDQogIGxhcHBseSh1bmxpc3QpDQoNCmZ1bmNfcGxvdF9hbm8gPC0gZnVuY3Rpb24oZGYsIHlfdmFyLCB5X3Zhcl9sYWIsIGxhYmVsLnlfc2V0ID0gNywgeXRpY2tzLmJ5X3NldCA9IDEsIGZhY2V0ID0gRkFMU0Upew0KICBpZihmYWNldCl7DQogICAgZmFjZXQgPC0gIlByb3RvY29sIg0KICB9ZWxzZXsNCiAgICBmYWNldCA8LSBOVUxMDQogIH0NCiAgcCA8LQ0KICAgIGRmICAlPiUNCiAgICBnZ3B1YnI6OmdnYmFycGxvdCh4ID0gIkFubyIsIHkgPSB5X3ZhciwgDQogICAgICAgICAgICAgICAgICAgICAgYWRkID0gIm1lYW5fc2UiLA0KICAgICAgICAgICAgICAgICAgICAgIGNvbG9yID0gImJsYWNrIiwgZmlsbCA9ICJsaWdodGdyYXkiLA0KICAgICAgICAgICAgICAgICAgICAgIHhsYWIgPSAiQW5vdGltcCIsIHlsYWIgPSB5X3Zhcl9sYWIsDQogICAgICAgICAgICAgICAgICAgICAgbGFiZWwgPSBUUlVFLCBsYWIubmIuZGlnaXRzID0gMiwgbGFiLnBvcz0gImluIiwNCiAgICAgICAgICAgICAgICAgICAgICBmYWNldC5ieSA9IGZhY2V0KSArDQogICAgc3RhdF9jb21wYXJlX21lYW5zKG1ldGhvZCA9ICJhbm92YSIsDQogICAgICAgICAgICAgICAgICAgICAgIGxhYmVsLnggPSAwLjksIGxhYmVsLnkgPSBsYWJlbC55X3NldCkgKw0KICAgIHN0YXRfY29tcGFyZV9tZWFucyhjb21wYXJpc29ucyA9IG15X2NvbXBhcmlzb25zLA0KICAgICAgICAgICAgICAgICAgICAgICBsYWJlbCA9ICJwLnNpZ25pZiIsIG1ldGhvZCA9ICJ0LnRlc3QiLCBwYWlyZWQgPSBGQUxTRSwgbmEucm0gPSBUUlVFKSANCiAgZ2dwYXIocCwgeXRpY2tzLmJ5ID0geXRpY2tzLmJ5X3NldCkgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyB0aGUgcmF0aW5nIHNjYWxlIGlzIDEtNw0KfQ0KDQoNCiMjIERvZGdlZCANCmZ1bmNfZG9kZ2VkX2FubyA8LSBmdW5jdGlvbihkZiwgeV92YXIsIHlfdmFyX2xhYiwgZmFjZXQgPSBGQUxTRSl7DQogIHlfdmFyPC0gc3ltKHlfdmFyKQ0KICANCiAgaWYoZmFjZXQpIHsNCiAgICBkZiA8LSANCiAgICAgIGRmICU+JSANCiAgICAgIG11dGF0ZShQcm90b2NvbCA9IHBhc3RlMCgiUHJvdG9jb2wgIiwgUHJvdG9jb2wpKSAlPiUNCiAgICAgIGdyb3VwX2J5KFByb3RvY29sKSAgDQogIH0NCiAgDQogIHAgPC0NCiAgICBkZiAgJT4lDQogICAgZHBseXI6OmNvdW50KEFubywgISF5X3ZhcikgJT4lICAgICAgICAgICAgICAgICAgICAgICAgIyBHcm91cCBieSwgdGhlbiBjb3VudCBudW1iZXIgaW4gZWFjaCBncm91cA0KICAgIG11dGF0ZShwY3QgPSBwcm9wLnRhYmxlKG4pKSAlPiUgICAgICAgICAgICAgICAgICAgICAjIENhbGN1bGF0ZSBwZXJjZW50IHdpdGhpbiBlYWNoIHZhcg0KICAgIG11dGF0ZShWYWxfZmFjID0gYXMuZmFjdG9yKCEheV92YXIpKSAlPiUNCiAgICBnZ3Bsb3QoYWVzKHggPSBBbm8sIHkgPSBwY3QsIGZpbGwgPSBWYWxfZmFjLCBsYWJlbCA9IHNjYWxlczo6cGVyY2VudChwY3QpKSkgKyANCiAgICAgIGdlb21fY29sKHBvc2l0aW9uID0gJ2RvZGdlJykgKyANCiAgICAgIGdlb21fdGV4dChwb3NpdGlvbiA9IHBvc2l0aW9uX2RvZGdlKHdpZHRoID0gLjkpLCAgICAjIG1vdmUgdG8gY2VudGVyIG9mIGJhcnMNCiAgICAgICAgICAgICAgICB2anVzdCA9IC0wLjUsICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIG51ZGdlIGFib3ZlIHRvcCBvZiBiYXINCiAgICAgICAgICAgICAgICBzaXplID0gMykgKyANCiAgICAgIHNjYWxlX3lfY29udGludW91cyhsYWJlbHMgPSBzY2FsZXM6OnBlcmNlbnQpICsNCiAgICAgIHtpZihmYWNldCkgZmFjZXRfd3JhcCh+UHJvdG9jb2wsIHNjYWxlcyA9ICJmcmVlIiwgbmNvbCA9IDEsIG5yb3cgPSA4KX0gKw0KICAgICAgZ2d0aXRsZSh5X3Zhcl9sYWIpICsNCiAgICAgIHhsYWIoIkFub3RpbXAiKSArIHlsYWIoIlBlcmNlbnRhZ2UgJSIpICsgDQogICAgICBndWlkZXMoZmlsbCA9IGd1aWRlX2xlZ2VuZCh0aXRsZSA9ICJWYWx1ZSIsIG5yb3cgPSAxKSkgKyANCiAgICAgIHNjYWxlX2ZpbGxfZ3JleShzdGFydCA9IDAuOCwgZW5kID0gMC4yLCBuYS52YWx1ZSA9ICJyZWQiLCBhZXN0aGV0aWNzID0gImZpbGwiKSArDQogICAgICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAiYm90dG9tIiwgbGVnZW5kLmRpcmVjdGlvbiA9ICJob3Jpem9udGFsIiwgDQogICAgICAgICAgICBsZWdlbmQuanVzdGlmaWNhdGlvbiA9IGMoMCwgMSksIHBhbmVsLmJvcmRlciA9IGVsZW1lbnRfcmVjdChmaWxsID0gTkEsIGNvbG91ciA9ICJibGFjayIpKQ0KICBwDQp9ICANCmBgYA0KDQoNCg0KIyMgUGxvdHMgb2YgU2Vhc29ucw0KDQpgYGB7ciBwbG90X3NlYW5zb24sIGZpZy5oZWlnaHQ9NywgZmlnLndpZHRoPTYsIGZpZy5hbGlnbj0nY2VudGVyJ30NCiMjIFRlc3QgZm9yIFZhbCAtLSB3b3JrcyB3ZWxsDQojIERhdGFfU2Vhc29uX21lbHRfbm9uYSAgJT4lDQojICAgZ2dwdWJyOjpnZ2JhcnBsb3QoeCA9ICJBbm8iLCB5ID0gIlZhbCIsIA0KIyAgICAgICAgICAgICAgICAgICAgIGFkZCA9ICJtZWFuX3NlIiwNCiMgICAgICAgICAgICAgICAgICAgICBjb2xvciA9ICJibGFjayIsIGZpbGwgPSAibGlnaHRncmF5IiwNCiMgICAgICAgICAgICAgICAgICAgICB4bGFiID0gIkFub3RpbXAiLCB5bGFiID0gIlZhbGVudGEiLA0KIyAgICAgICAgICAgICAgICAgICAgIGxhYmVsID0gVFJVRSwgbGFiLm5iLmRpZ2l0cyA9IDIsIGxhYi5wb3M9ICJpbiIpICsNCiMgICBzdGF0X2NvbXBhcmVfbWVhbnMobWV0aG9kID0gImFub3ZhIiwNCiMgICAgICAgICAgICAgICAgICAgICAgbGFiZWwueCA9IDAuOSwgbGFiZWwueSA9IDcpICsNCiMgICBzdGF0X2NvbXBhcmVfbWVhbnMoY29tcGFyaXNvbnMgPSBteV9jb21wYXJpc29ucywNCiMgICAgICAgICAgICAgICAgICAgICAgbGFiZWwgPSAicC5zaWduaWYiLCBtZXRob2QgPSAidC50ZXN0IiwgcGFpcmVkID0gRkFMU0UsIG5hLnJtID0gVFJVRSkgDQoNCg0KZnVuY19wbG90X2FubyhEYXRhX1NlYXNvbl9tZWx0X25vbmEsICJWYWwiLCAiVmFsZW50YSIpDQpmdW5jX3Bsb3RfYW5vKERhdGFfU2Vhc29uX21lbHRfbm9uYSwgIlJlbHYiLCAiUmVsZXZhbnRhIHBlcnNvbmFsYSIpDQpmdW5jX3Bsb3RfYW5vKERhdGFfU2Vhc29uX21lbHRfbm9uYSwgIlZpdiIsICJWaXZpZCIpDQpmdW5jX3Bsb3RfYW5vKERhdGFfU2Vhc29uX21lbHRfbm9uYSwgIlZhcnN0YWFtaW4iLCAiVmFyc3RhIGFtaW50aXJlIiwgbGFiZWwueV9zZXQgPSA1MCwgeXRpY2tzLmJ5X3NldCA9IDUpDQpgYGANCg0KDQojIyBQbG90cyBvZiBTZWFzb25zIGJ5IFByb3RvY29sDQoNCmBgYHtyIHBsb3Rfc2VhbnNvbjIsIGZpZy5oZWlnaHQ9MTAsIGZpZy53aWR0aD0xMiwgZmlnLmFsaWduPSdjZW50ZXInfQ0KZnVuY19wbG90X2FubyhEYXRhX1NlYXNvbl9tZWx0X25vbmEsICJWYWwiLCAiVmFsZW50YSIsIGZhY2V0ID0gVFJVRSkgDQpmdW5jX3Bsb3RfYW5vKERhdGFfU2Vhc29uX21lbHRfbm9uYSwgIlJlbHYiLCAiUmVsZXZhbnRhIHBlcnNvbmFsYSIsIGZhY2V0ID0gVFJVRSkNCmZ1bmNfcGxvdF9hbm8oRGF0YV9TZWFzb25fbWVsdF9ub25hLCAiVml2IiwgIlZpdmlkIiwgZmFjZXQgPSBUUlVFKQ0KZnVuY19wbG90X2FubyhEYXRhX1NlYXNvbl9tZWx0X25vbmEsICJWYXJzdGFhbWluIiwgIlZhcnN0YSBhbWludGlyZSIsIGxhYmVsLnlfc2V0ID0gNTAsIHl0aWNrcy5ieV9zZXQgPSA1LCBmYWNldCA9IFRSVUUpDQpgYGANCg0KDQojIyBQbG90cyB3aXRoIHByb3BvcnRpb24gb2YgdmFsdWVzDQoNCmBgYHtyIHBsb3Rfc2VhbnNvbl9wcm9wX3N0YWNrZWQsIGZpZy5oZWlnaHQ9OSwgZmlnLndpZHRoPTgsIGZpZy5hbGlnbj0nY2VudGVyJywgcmVzdWx0cz0nYXNpcyd9DQojIFN0YWNrZWQgLSBUZXN0IGZvciBWYWwgLS0gd29ya3Mgd2VsbA0KY2F0KCIjIyMgU3RhY2tlZDogVmFsIikNCkRhdGFfU2Vhc29uX21lbHRfbm9uYSAlPiUNCiAgZHBseXI6OmNvdW50KEFubywgVmFsKSAlPiUgICAgICAgICAgICAgICAgICAgIyBHcm91cCBieSwgdGhlbiBjb3VudCBudW1iZXIgaW4gZWFjaCBncm91cA0KICBtdXRhdGUocGN0ID0gbi9zdW0obikpICU+JSAgICAgICAgICAgICAgICAgICAjIENhbGN1bGF0ZSBwZXJjZW50IHdpdGhpbiBlYWNoIHZhcjsgY291bGQgdXNlIHByb3AudGFibGUobikNCiAgbXV0YXRlKFZhbF9mYWMgPSBhcy5mYWN0b3IoVmFsKSkgJT4lDQpnZ3Bsb3QoYWVzKEFubywgbiwgZmlsbCA9IFZhbF9mYWMpKSArDQogIGdlb21fYmFyKHN0YXQgPSAiaWRlbnRpdHkiKSArICANCiAgZ2VvbV90ZXh0KGFlcyhsYWJlbCA9IHBhc3RlMChzcHJpbnRmKCIlMS4xZiIsIHBjdCoxMDApLCAiJSIpLCBzaXplID0gc2NhbGVzOjpyZXNjYWxlKHBjdCwgdG89YygyLCA1KSkpLA0KICAgICAgICAgICAgcG9zaXRpb24gPSBwb3NpdGlvbl9zdGFjayh2anVzdD0wLjUpLCBzaG93LmxlZ2VuZCA9IEZBTFNFKSArDQogIGd1aWRlcyhmaWxsID0gZ3VpZGVfbGVnZW5kKHRpdGxlID0gIlZhbHVlIiwgbnJvdyA9IDEpKSArIA0KICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAiYm90dG9tIiwgbGVnZW5kLmRpcmVjdGlvbiA9ICJob3Jpem9udGFsIiwgbGVnZW5kLmp1c3RpZmljYXRpb24gPSBjKDAsIDEpKQ0KDQpgYGANCg0KDQpgYGB7ciBwbG90X3NlYW5zb25fcHJvcF9kb2RnZSwgZmlnLmhlaWdodD04LCBmaWcud2lkdGg9MTIsIGZpZy5hbGlnbj0nY2VudGVyJywgcmVzdWx0cz0nYXNpcyd9DQojICMgRG9kZ2VkIC0gVGVzdCBmb3IgVmFsIC0tIHdvcmtzIHdlbGwNCiMgRGF0YV9TZWFzb25fbWVsdF9ub25hICU+JSANCiMgICBkcGx5cjo6Y291bnQoQW5vLCBWYWwpICU+JSAgICAgICAgICAgICAgICAgICAgICAgICMgR3JvdXAgYnksIHRoZW4gY291bnQgbnVtYmVyIGluIGVhY2ggZ3JvdXANCiMgICBtdXRhdGUocGN0ID0gcHJvcC50YWJsZShuKSkgJT4lICAgICAgICAgICAgICAgICAgICMgQ2FsY3VsYXRlIHBlcmNlbnQgd2l0aGluIGVhY2ggdmFyDQojICAgbXV0YXRlKFZhbF9mYWMgPSBhcy5mYWN0b3IoVmFsKSkgJT4lDQojICAgZ2dwbG90KGFlcyh4ID0gQW5vLCB5ID0gcGN0LCBmaWxsID0gVmFsX2ZhYywgbGFiZWwgPSBzY2FsZXM6OnBlcmNlbnQocGN0KSkpICsgDQojICAgICBnZW9tX2NvbChwb3NpdGlvbiA9ICdkb2RnZScpICsgDQojICAgICBnZW9tX3RleHQocG9zaXRpb24gPSBwb3NpdGlvbl9kb2RnZSh3aWR0aCA9IC45KSwgICAgIyBtb3ZlIHRvIGNlbnRlciBvZiBiYXJzDQojICAgICAgICAgICAgICAgdmp1c3QgPSAtMC41LCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBudWRnZSBhYm92ZSB0b3Agb2YgYmFyDQojICAgICAgICAgICAgICAgc2l6ZSA9IDMpICsgDQojICAgICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzID0gc2NhbGVzOjpwZXJjZW50KSArDQojICAgICB4bGFiKCJBbm90aW1wIikgKyB5bGFiKCJQZXJjZW50YWdlICUiKSArIA0KIyAgICAgZ3VpZGVzKGZpbGwgPSBndWlkZV9sZWdlbmQodGl0bGUgPSAiVmFsdWUiLCBucm93ID0gMSkpICsgDQojICAgICBzY2FsZV9maWxsX2dyZXkoc3RhcnQgPSAwLjgsIGVuZCA9IDAuMiwgbmEudmFsdWUgPSAicmVkIiwgYWVzdGhldGljcyA9ICJmaWxsIikgKw0KIyAgICAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gImJvdHRvbSIsIGxlZ2VuZC5kaXJlY3Rpb24gPSAiaG9yaXpvbnRhbCIsIGxlZ2VuZC5qdXN0aWZpY2F0aW9uID0gYygwLCAxKSkgDQogIA0KY2F0KCIjIyMgRG9kZ2VkOiBWYWwsIFJlbGV2LCBWaXZpZCIpDQpmdW5jX2RvZGdlZF9hbm8oRGF0YV9TZWFzb25fbWVsdF9ub25hLCAiVmFsIiwgIlZhbGVudGEiKQ0KZnVuY19kb2RnZWRfYW5vKERhdGFfU2Vhc29uX21lbHRfbm9uYSwgIlJlbHYiLCAiUmVsZXZhbnRhIHBlcnNvbmFsYSIpDQpmdW5jX2RvZGdlZF9hbm8oRGF0YV9TZWFzb25fbWVsdF9ub25hLCAiVml2IiwgIlZpdmlkIikNCmBgYA0KDQoNCiMjIFBsb3RzIHdpdGggcHJvcG9ydGlvbiBvZiB2YWx1ZXMgYnkgUHJvdG9jb2wNCg0KYGBge3IgcGxvdF9zZWFuc29uX3Byb3AyLCBmaWcuaGVpZ2h0PTI1LCBmaWcud2lkdGg9MTAsIGZpZy5hbGlnbj0nY2VudGVyJ30NCmZ1bmNfZG9kZ2VkX2FubyhEYXRhX1NlYXNvbl9tZWx0X25vbmEsICJWYWwiLCAiVmFsZW50YSIsIGZhY2V0ID0gVFJVRSkNCmZ1bmNfZG9kZ2VkX2FubyhEYXRhX1NlYXNvbl9tZWx0X25vbmEsICJSZWx2IiwgIlJlbGV2YW50YSBwZXJzb25hbGEiLCBmYWNldCA9IFRSVUUpDQpmdW5jX2RvZGdlZF9hbm8oRGF0YV9TZWFzb25fbWVsdF9ub25hLCAiVml2IiwgIlZpdmlkIiwgZmFjZXQgPSBUUlVFKQ0KYGBgDQoNCg0KIyMgTGlrZXJ0IFBsb3RzIGZvciBTZWFzb24NCg0KYGBge3IgcGxvdF9saWtlcnQsIHJlc3VsdHM9J2FzaXMnLCBmaWcuaGVpZ2h0PTYsIGZpZy53aWR0aD04LCBmaWcuYWxpZ249J2NlbnRlcid9DQojIFByb3BvcnRpb25zIGFuZCB6LXNjb3Jlcw0KUHJvcF92YWwgPC0gDQogIERhdGFfU2Vhc29uX21lbHRfbm9uYSAlPiUNCiAgICBkcGx5cjo6c2VsZWN0KElELCBQcm90b2NvbCwgQW5vLCBWYWwpICU+JQ0KICAgIGdyb3VwX2J5KEFubykgJT4lDQogICAgbXV0YXRlKA0KICAgICAgVmFsID0gYXMuZmFjdG9yKFZhbCksDQogICAgICBWYWwgPSBmb3JjYXRzOjpmY3RfY29sbGFwc2UoVmFsLCBsb3cgPSBjKCIxIiwgIjIiLCAiMyIpLCBuZXV0cmFsID0gIjQiLCBoaWdoID0gYygiNSIsICI2IiwgIjciKSkNCiAgICAgICkgJT4lDQogICAgZHBseXI6OmNvdW50KFZhbCkgJT4lIA0KICAgIG11dGF0ZSh0b3RhbCA9IHN1bShuKSwNCiAgICAgICAgICAgcGVyYyA9IDEwMCpuL3RvdGFsKQ0KDQpjYXQoIiMjIyBQcm9wb3J0aW9ucyAtIGNvbXBhcmVkIHRvIDAuNSBwcm9iYWJpbGl0eSIpDQpQcm9wX3ZhbCAlPiUNCmZpbHRlcihWYWwgPT0gImhpZ2giKSAlPiUNCiAgcm93d2lzZSAlPiUNCiAgbXV0YXRlKHRzdCA9IGxpc3QoYnJvb206OnRpZHkocHJvcC50ZXN0KG4sIHRvdGFsLCBjb25mLmxldmVsID0gMC45NSkpKSkgJT4lDQogIHRpZHlyOjp1bm5lc3QodHN0KQ0KDQpQcm9wX3ZhbCAlPiUNCmZpbHRlcihWYWwgPT0gImxvdyIpICU+JQ0KICByb3d3aXNlICU+JQ0KICBtdXRhdGUodHN0ID0gbGlzdChicm9vbTo6dGlkeShwcm9wLnRlc3QobiwgdG90YWwsIGNvbmYubGV2ZWwgPSAwLjk1KSkpKSAlPiUgDQogIHRpZHlyOjp1bm5lc3QodHN0KQ0KDQoNCmNhdCgiIyMjIFByb3BvcnRpb25zIC0gTXVsdGlwbGUgY29tcGFyaXNvbnMiKQ0KUGFpcl9Db21wX3Byb3BfaGlnaCA8LSAgICAgICAjIGNvbXBhaXJlIGFsbCBwcm9wb3J0aW9ucyBwYWlyd2lzZQ0KICBQcm9wX3ZhbCAlPiUNCiAgZmlsdGVyKFZhbCA9PSAiaGlnaCIpICU+JQ0KICBkcGx5cjo6c2VsZWN0KC1wZXJjKSAlPiUNCiAgdW5pdGUoIkNhdGVnIiwgYygiQW5vIiwgIlZhbCIpLCBzZXAgPSAiLSIpICU+JQ0KICBjb2x1bW5fdG9fcm93bmFtZXMoIkNhdGVnIikgDQoNClBhaXJfQ29tcF9wcm9wX2hpZ2hfbWF0IDwtDQogIFBhaXJfQ29tcF9wcm9wX2hpZ2ggJT4lDQogICAgcm93bmFtZXNfdG9fY29sdW1uKCJyb3duYW1lIikgJT4lDQogICAgZHBseXI6OnJlbmFtZShzdWNjZXNzID0gbikgJT4lDQogICAgbXV0YXRlKGZhaWx1cmUgPSB0b3RhbCAtIHN1Y2Nlc3MpICU+JQ0KICAgIGRwbHlyOjpzZWxlY3QoLXRvdGFsKSAlPiUNCiAgICBjb2x1bW5fdG9fcm93bmFtZXMoInJvd25hbWUiKSAlPiUNCiAgICBhcy5tYXRyaXgoKSANCg0KUGFpcl9Db21wX3Byb3BfbG93IDwtICAgICAgICMgY29tcGFpcmUgYWxsIHByb3BvcnRpb25zIHBhaXJ3aXNlDQogIFByb3BfdmFsICU+JQ0KICBmaWx0ZXIoVmFsID09ICJsb3ciKSAlPiUNCiAgZHBseXI6OnNlbGVjdCgtcGVyYykgJT4lDQogIHVuaXRlKCJDYXRlZyIsIGMoIkFubyIsICJWYWwiKSwgc2VwID0gIi0iKSAlPiUNCiAgY29sdW1uX3RvX3Jvd25hbWVzKCJDYXRlZyIpIA0KDQpQYWlyX0NvbXBfcHJvcF9sb3dfbWF0IDwtDQogIFBhaXJfQ29tcF9wcm9wX2xvdyAlPiUNCiAgcm93bmFtZXNfdG9fY29sdW1uKCJyb3duYW1lIikgJT4lDQogIGRwbHlyOjpyZW5hbWUoc3VjY2VzcyA9IG4pICU+JQ0KICBtdXRhdGUoZmFpbHVyZSA9IHRvdGFsIC0gc3VjY2VzcykgJT4lDQogIGRwbHlyOjpzZWxlY3QoLXRvdGFsKSAlPiUNCiAgY29sdW1uX3RvX3Jvd25hbWVzKCJyb3duYW1lIikgJT4lDQogIGFzLm1hdHJpeCgpDQoNCmNhdCgiIyMjIyBQYWlyd2lzZSBjb21wYXJpc29ucyB1c2luZyBQYWlyd2lzZSBjb21wYXJpc29uIG9mIHByb3BvcnRpb25zIikNCnBhaXJ3aXNlLnByb3AudGVzdCh4ID0gUGFpcl9Db21wX3Byb3BfaGlnaF9tYXQsIHAuYWRqdXN0Lm1ldGhvZCA9ICJub25lIikgJT4lIA0KICB0aWR5KCkNCnBhaXJ3aXNlLnByb3AudGVzdCh4ID0gUGFpcl9Db21wX3Byb3BfbG93X21hdCwgcC5hZGp1c3QubWV0aG9kID0gIm5vbmUiKSAlPiUgDQogIHRpZHkoKQ0KDQpjYXQoIiMjIyMgUGFpcndpc2UgY29tcGFyaXNvbnMgdXNpbmcgUGFpcndpc2UgY29tcGFyaXNvbiBvZiBwcm9wb3J0aW9ucyAoRmlzaGVyIGV4YWN0KSIpICAgICMgbGlicmFyeShmbXNiKQ0KZm1zYjo6cGFpcndpc2UuZmlzaGVyLnRlc3QoeCA9IFBhaXJfQ29tcF9wcm9wX2hpZ2hfbWF0LCBwLmFkanVzdC5tZXRob2QgPSAibm9uZSIpICU+JSANCiAgdGlkeSgpDQpmbXNiOjpwYWlyd2lzZS5maXNoZXIudGVzdCh4ID0gUGFpcl9Db21wX3Byb3BfbG93X21hdCwgcC5hZGp1c3QubWV0aG9kID0gIm5vbmUiKSAlPiUgDQogIHRpZHkoKQ0KDQoNCiMgbGlicmFyeShwYWlyY29tcHZpeikNCiMgcGFpcmNvbXB2aXo6OnBhaXJjb21wKFBhaXJfQ29tcF9wcm9wX2hpZ2gkbiwgUGFpcl9Db21wX3Byb3BfaGlnaCR0b3RhbCwgY29ycmVjdCA9IEZBTFNFLA0KIyAgICAgICAgICAgICAgICAgICAgICAgdGVzdCA9ICJwcm9wIiwgcmVzdWx0ID0gVFJVRSwgcC5hZGp1c3QubWV0aG9kID0gIm5vbmUiKSANCg0KIyBEYXRhIGZvciBQbG90DQpjYXQoIiMjIyBQcm9wb3J0aW9ucyAtIFBsb3Qgb2YgTG93LU5ldXRyYWwtSGlnaCIpDQpMaWtlcnRfdmFsIDwtIA0KICBEYXRhX1NlYXNvbl9tZWx0X25vbmEgJT4lDQogIGRwbHlyOjpzZWxlY3QoSUQsIFByb3RvY29sLCBncm91cCwgQW5vLCBWYWwpICU+JQ0KICBzcHJlYWQoa2V5ID0gQW5vLCB2YWx1ZSA9IFZhbCkgJT4lDQogIG11dGF0ZV9hdCh2YXJzKCJWYXJhIiwgIlByaW1hdmFyYSIsICJUb2FtbmEiLCAiSWFybmEiKSwgfmFzLmZhY3RvciguKSkNCg0KIyBQbG90cyAgIyBsaWJyYXJ5KGxpa2VydCkNCkxpa2VydG9ial9WYWwgPC0gbGlrZXJ0KExpa2VydF92YWxbLCBjKCJWYXJhIiwgIlByaW1hdmFyYSIsICJUb2FtbmEiLCAiSWFybmEiKV0sIG5sZXZlbHMgPSA3KSAgICMgaGVyZSBhcmUgcGVyY2VudGFnZXMNCkxpa2VydG9ial9WYWxfcGVyYyA8LSBMaWtlcnRvYmpfVmFsJHJlc3VsdHMNCiMgY2hlY2sgaWYgc2FtZSB3aXRoIFByb3AgZGF0YWZyYW1lIGFib3ZlOyBvciBwcm9wLnRhYmxlKHRhYmxlKExpa2VydF92YWwkVmFyYSkpDQoNCnBsb3QoTGlrZXJ0b2JqX1ZhbCwgdHlwZSA9ICJiYXIiLCANCiAgICAgY2VudGVyZWQgPSBUUlVFLCBjZW50ZXIgPSA0LCBpbmNsdWRlLmNlbnRlciA9IFRSVUUsICAgICAgICAgICAgICAjICI0IiBpcyBuZXV0cmFsDQogICAgIHdyYXAgPSAzMCwgbG93LmNvbG9yID0gJ2J1cmx5d29vZCcsIGhpZ2guY29sb3IgPSAnbWFyb29uJykgKw0KICBndWlkZXMoZmlsbCA9IGd1aWRlX2xlZ2VuZChucm93ID0gMSkpDQoNCmBgYA0KDQoNCiMjIFJlbGF0aW9uc2hpcDogRnJlcXVlbmN5IG9mIHJlbWVtYmVyaW5nIC0gVmFsZW5jZQ0KDQpgYGB7ciByZWxfQW5vZnJlcV9WYWwsIHJlc3VsdHM9J2FzaXMnLCBmaWcuaGVpZ2h0PTcsIGZpZy53aWR0aD03LCBmaWcuYWxpZ249J2NlbnRlcid9DQpBbm9mcmVxX1ZhbCA8LSANCiAgRGF0YV9TZWFzb25fbWVsdF9ub25hICU+JQ0KICAgIGRwbHlyOjpzZWxlY3QoSUQsIFByb3RvY29sLCBBbm8sIFZhbCkgJT4lDQogICAgZ3JvdXBfYnkoSUQsIEFubykgJT4lDQogICAgZHBseXI6OnN1bW1hcml6ZShNZWFuX1ZhbCA9IG1lYW4oVmFsLCBuYS5ybT1UUlVFKSwNCiAgICAgICAgICAgICAgICAgICAgIEZyZXFfQW5vID0gbigpKSANCg0KY2F0KCIjIyMgU2NhdHRlciBwbG90IHdpdGggY29ycmVsYXRpb24gY29lZmZpY2llbnQgZm9yIGFsbCBTZWFzb25zIikNCmdncHVicjo6Z2dzY2F0dGVyKEFub2ZyZXFfVmFsLCB4ID0gIkZyZXFfQW5vIiwgeSA9ICJNZWFuX1ZhbCIsDQogICAgICAgICAgICBhZGQgPSAicmVnLmxpbmUiLCAgDQogICAgICAgICAgICBhZGQucGFyYW1zID0gbGlzdChjb2xvciA9ICJibHVlIiwgZmlsbCA9ICJsaWdodGdyYXkiKSwgDQogICAgICAgICAgICBjb25mLmludCA9IFRSVUUgKSArDQpzdGF0X2NvcihtZXRob2QgPSAicGVhcnNvbiIsIGxhYmVsLnggPSA3LCBsYWJlbC55ID0gMTApDQoNCg0KY2F0KCIjIyMgU2NhdHRlciBwbG90IHdpdGggY29ycmVsYXRpb24gY29lZmZpY2llbnQgZm9yIGVhY2ggU2Vhc29uIikNCmdncHVicjo6Z2dzY2F0dGVyKEFub2ZyZXFfVmFsLCB4ID0gIkZyZXFfQW5vIiwgeSA9ICJNZWFuX1ZhbCIsDQogICBjb2xvciA9ICJBbm8iLCBwYWxldHRlID0gImpjbyIsDQogICBhZGQgPSAicmVnLmxpbmUiLCBjb25mLmludCA9IFRSVUUsDQogICB4bGltID0gYygwLCAxNSksIHlsaW0gPSBjKDAsIDgpKSArIA0Kc3RhdF9jb3IoYWVzKGNvbG9yID0gQW5vKSwgbWV0aG9kID0gInBlYXJzb24iLCBsYWJlbC54ID0gMTEpDQoNCmBgYA0KDQoNCiMjIE1vc3QgbmVnYXRpdmUgVmFsZW5jZSAob25lcykNCg0KYGBge3IgcGxvdF9saWtlcnRfb25lcywgcmVzdWx0cz0nYXNpcycsIGZpZy5oZWlnaHQ9MTAsIGZpZy53aWR0aD0xMCwgZmlnLmFsaWduPSdjZW50ZXInfQ0KIyBQcm9wb3J0aW9ucyBhbmQgei1zY29yZXMNClByb3BfdmFsMiA8LSANCiAgRGF0YV9TZWFzb25fbWVsdF9ub25hICU+JQ0KICAgIGRwbHlyOjpzZWxlY3QoSUQsIFByb3RvY29sLCBBbm8sIFZhbCkgJT4lDQogICAgZ3JvdXBfYnkoQW5vKSAlPiUNCiAgICBkcGx5cjo6Y291bnQoVmFsKSAlPiUgDQogICAgbXV0YXRlKHRvdGFsID0gc3VtKG4pLA0KICAgICAgICAgICBwZXJjID0gMTAwKm4vdG90YWwpDQoNCmNhdCgiIyMjIFByb3BvcnRpb25zIC0gY29tcGFyZWQgdG8gMC41IHByb2JhYmlsaXR5IikNClByb3BfdmFsMiAlPiUNCmZpbHRlcihWYWwgPT0gMSkgJT4lDQogIHJvd3dpc2UgJT4lDQogIG11dGF0ZSh0c3QgPSBsaXN0KGJyb29tOjp0aWR5KHByb3AudGVzdChuLCB0b3RhbCwgY29uZi5sZXZlbCA9IDAuOTUpKSkpICU+JQ0KICB0aWR5cjo6dW5uZXN0KHRzdCkNCg0KY2F0KCIjIyMgUHJvcG9ydGlvbnMgLSBNdWx0aXBsZSBjb21wYXJpc29ucyIpDQpQYWlyX0NvbXBfcHJvcF9vbmVzIDwtICAgICAgICMgY29tcGFpcmUgYWxsIHByb3BvcnRpb25zIHBhaXJ3aXNlDQogIFByb3BfdmFsMiAlPiUNCiAgZmlsdGVyKFZhbCA9PSAxKSAlPiUNCiAgZHBseXI6OnNlbGVjdCgtcGVyYykgJT4lDQogIHVuaXRlKCJDYXRlZyIsIGMoIkFubyIsICJWYWwiKSwgc2VwID0gIi0iKSAlPiUNCiAgY29sdW1uX3RvX3Jvd25hbWVzKCJDYXRlZyIpIA0KDQpQYWlyX0NvbXBfcHJvcF9vbmVzX21hdCA8LQ0KICBQYWlyX0NvbXBfcHJvcF9vbmVzICU+JQ0KICAgIHJvd25hbWVzX3RvX2NvbHVtbigicm93bmFtZSIpICU+JQ0KICAgIGRwbHlyOjpyZW5hbWUoc3VjY2VzcyA9IG4pICU+JQ0KICAgIG11dGF0ZShmYWlsdXJlID0gdG90YWwgLSBzdWNjZXNzKSAlPiUNCiAgICBkcGx5cjo6c2VsZWN0KC10b3RhbCkgJT4lDQogICAgY29sdW1uX3RvX3Jvd25hbWVzKCJyb3duYW1lIikgJT4lDQogICAgYXMubWF0cml4KCkgDQoNCmNhdCgiIyMjIyBQYWlyd2lzZSBjb21wYXJpc29ucyB1c2luZyBQYWlyd2lzZSBjb21wYXJpc29uIG9mIHByb3BvcnRpb25zIikNCnBhaXJ3aXNlLnByb3AudGVzdCh4ID0gUGFpcl9Db21wX3Byb3Bfb25lc19tYXQsIHAuYWRqdXN0Lm1ldGhvZCA9ICJub25lIikgJT4lIA0KICB0aWR5KCkNCg0KY2F0KCIjIyMjIFBhaXJ3aXNlIGNvbXBhcmlzb25zIHVzaW5nIFBhaXJ3aXNlIGNvbXBhcmlzb24gb2YgcHJvcG9ydGlvbnMgKEZpc2hlciBleGFjdCkiKSAgICAjIGxpYnJhcnkoZm1zYikNCmZtc2I6OnBhaXJ3aXNlLmZpc2hlci50ZXN0KHggPSBQYWlyX0NvbXBfcHJvcF9vbmVzX21hdCwgcC5hZGp1c3QubWV0aG9kID0gIm5vbmUiKSAlPiUgDQogIHRpZHkoKQ0KDQojIERhdGEgZm9yIFBsb3QNCmNhdCgiIyMjIFByb3BvcnRpb25zIC0gUGxvdCBvZiBMb3ctTmV1dHJhbC1IaWdoIikNCkxpa2VydF92YWxfYWxsIDwtIA0KICBEYXRhX1NlYXNvbl9tZWx0X25vbmEgJT4lDQogIGRwbHlyOjpzZWxlY3QoSUQsIFByb3RvY29sLCBncm91cCwgQW5vLCBWYWwpICU+JQ0KICBzcHJlYWQoa2V5ID0gQW5vLCB2YWx1ZSA9IFZhbCkgJT4lDQogIG11dGF0ZV9hdCh2YXJzKCJWYXJhIiwgIlByaW1hdmFyYSIsICJUb2FtbmEiLCAiSWFybmEiKSwgfmFzLmZhY3RvciguKSkNCg0KIyBQbG90cyAgIyBsaWJyYXJ5KGxpa2VydCkNCkxpa2VydG9ial9WYWxfYWxsIDwtIGxpa2VydChMaWtlcnRfdmFsWywgYygiVmFyYSIsICJQcmltYXZhcmEiLCAiVG9hbW5hIiwgIklhcm5hIildLCBubGV2ZWxzID0gNykgICAjIGhlcmUgYXJlIHBlcmNlbnRhZ2VzDQpMaWtlcnRvYmpfVmFsX3BlcmNfYWxsIDwtIExpa2VydG9ial9WYWwkcmVzdWx0cw0KDQpwbG90KExpa2VydG9ial9WYWxfYWxsLCB0eXBlID0gImhlYXQiKSArIA0KICBnZ3RpdGxlKCJNZWFuIChTRCkgdnMgUGVyY2V0YWdlcyBvZiBWYWxlbmNlIFJhdGluZ3MiKSArIA0KICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAnbm9uZScpDQoNCmBgYA0KDQoNCg0KDQoNCg0KDQo8YnI+DQoNCg0KDQoNCjwhLS0gU2Vzc2lvbiBJbmZvIGFuZCBMaWNlbnNlIC0tPg0KDQo8YnI+DQoNCiMgU2Vzc2lvbiBJbmZvDQpgYGB7ciBzZXNzaW9uX2luZm8sIGVjaG8gPSBGQUxTRSwgcmVzdWx0cyA9ICdtYXJrdXAnfQ0Kc2Vzc2lvbkluZm8oKSAgICANCmBgYA0KDQo8IS0tIEZvb3RlciAtLT4NCiZuYnNwOw0KPGhyIC8+DQo8cCBzdHlsZT0idGV4dC1hbGlnbjogY2VudGVyOyI+QSB3b3JrIGJ5IDxhIGhyZWY9Imh0dHBzOi8vZ2l0aHViLmNvbS9DbGF1ZGl1UGFwYXN0ZXJpLyI+Q2xhdWRpdSBQYXBhc3Rlcmk8L2E+PC9wPg0KPHAgc3R5bGU9InRleHQtYWxpZ246IGNlbnRlcjsiPjxzcGFuIHN0eWxlPSJjb2xvcjogIzgwODA4MDsiPjxlbT5jbGF1ZGl1LnBhcGFzdGVyaUBnbWFpbC5jb208L2VtPjwvc3Bhbj48L3A+DQombmJzcDsNCg==